home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 14 / CU Amiga Magazine's Super CD-ROM 14 (1997)(EMAP Images)(GB)(Track 1 of 3)[!][issue 1997-09].iso / CUCD / Programming / SAS-C / sc655pch / amiproc / amiproc.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-01-09  |  10.3 KB  |  316 lines

  1.  
  2. /* Copyright (c) 1989-1994, Steve Krueger and Doug Walker, Raleigh, NC. */
  3. /* All Rights Reserved.                                                 */
  4.  
  5. /* NOTE: Define the preprocessor symbol NOOLDDOS when compiling to */
  6. /* save a little space if your program will never be run under     */
  7. /* AmigaDOS 1.3 or earlier.                                        */
  8.  
  9. /* We need _USEOLDEXEC_ to ensure that the extern SysBase isn't used */
  10. /* to access exec.library.  Since we're mucking about with our near  */
  11. /* data register, we don't want to rely on near data to make exec    */
  12. /* calls!                                                            */
  13. #define _USEOLDEXEC_ 1
  14.  
  15. #include <exec/memory.h>
  16. #include <exec/execbase.h>
  17. #include <libraries/dosextens.h>
  18. #include <dos/dostags.h>
  19. #include <proto/exec.h>
  20. #include <proto/dos.h>
  21.  
  22. #include <dos.h>
  23. #include <string.h>
  24.  
  25. #include "amiproc.h"
  26.  
  27. // Functions defined in the SAS/C® library to run autoinitializer
  28. // and autoterminater functions
  29. void __stdargs __fpinit(void);
  30. void __stdargs __fpterm(void);
  31.  
  32. // Data items defined in the SAS/C® library and possibly overridden
  33. // by user code.  __stack gives the desired stack size, __priority
  34. // gives the desired priority, and __procname gives the desired name
  35. // for any new processes.
  36. extern long __stack, __priority;
  37. extern char *__procname;
  38.  
  39. extern char __far RESLEN;               /* size of init data   */
  40. extern char __far RESBASE;              /* Base of global data */
  41. extern char __far NEWDATAL;             /* size of global data */
  42. extern const char __far LinkerDB;       /* Original A4 value   */
  43. extern struct DosLibrary *DOSBase;
  44. extern char *_ProgramName;
  45. extern struct ExecBase *SysBase;
  46. BPTR __curdir;
  47.  
  48. static struct DosLibrary *MyDOSBase;
  49.  
  50. #define DATAWORDS ((ULONG)&NEWDATAL)     /* magic to get right type of reloc */ 
  51.  
  52. static long _CloneData(void)
  53. {
  54.    ULONG *newa4;
  55.    ULONG *origa4;
  56.    ULONG *reloc;
  57.    ULONG nrelocs;
  58.    struct WBStartup *wbtmp = _WBenchMsg;
  59.    char *pntmp = _ProgramName;
  60.    BPTR cdtmp = __curdir;
  61.  
  62.    // Allocate the new data section
  63.    newa4 = (ULONG *)AllocMem((ULONG)&RESLEN, MEMF_PUBLIC);
  64.    if(newa4 == NULL) return NULL;
  65.  
  66.    // Get original A4 value
  67.    // This points to the UNMODIFIED near global data section
  68.    // allocated by the cres.o startup.  This line of code 
  69.    // will also generate a linker warning; ignore it.
  70.    origa4 = (ULONG *)((ULONG)&LinkerDB - (ULONG)&RESBASE);
  71.  
  72.    // Copy over initialized data
  73.    memcpy(newa4, origa4, DATAWORDS*4);
  74.    
  75.    // Zero uninitialized data
  76.    memset(newa4+DATAWORDS, 0, (((ULONG)&RESLEN)-DATAWORDS*4));
  77.  
  78.    // Perform relocations
  79.    // The number of relocs is stashed immediately after the
  80.    // initialized data in the original data section.  The
  81.    // relocs themselves follow.
  82.    origa4 += DATAWORDS;
  83.    for(nrelocs = *origa4++; nrelocs>0; nrelocs--)
  84.    {
  85.       reloc = (ULONG *)((ULONG)newa4 + *origa4++);
  86.       *reloc += (ULONG)newa4;
  87.    }
  88.  
  89.    // If your code has >32k of near data, RESBASE will be 32k.
  90.    // Otherwise, it will be 0.  The A4 pointer must point into
  91.    // the middle of the data section if you have >32k of data
  92.    // because the A4-relative addressing mode can handle +/-32k
  93.    // of data, not 64k of positive data.
  94.    newa4 += (ULONG)&RESBASE;
  95.    putreg(REG_A4, (long)newa4);
  96.  
  97.    // Set up a couple of externs that the startup code normally
  98.    // does for you...
  99.    SysBase = *(struct ExecBase **)4;
  100.    MyDOSBase = DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 0L);
  101.    _WBenchMsg = wbtmp;       // Copied from old data section
  102.    _ProgramName = pntmp;     // Copied from old data section
  103.    __curdir = DupLock(cdtmp);// cdtmp copied from old data section
  104.  
  105.    return (long)newa4;
  106. }
  107.  
  108. static void _FreeData(void)
  109. {
  110.    // Free the current directory lock
  111.    UnLock(__curdir);
  112.  
  113.    // Close the local static copy of MyDOSBase
  114.    CloseLibrary((struct Library *)MyDOSBase);
  115.    
  116.    // Free the new data section we allocated
  117.    FreeMem((void *)getreg(REG_A4), (ULONG)&RESLEN);
  118. }
  119.  
  120. struct FAKE_SegList {
  121.    long space;
  122.    long length;
  123.    BPTR nextseg;
  124.     short jmp;
  125.     void (*func)(void);
  126. };
  127.  
  128. struct AmiProcMsg {                     /* startup message sent to child */
  129.         struct Message msg;
  130.         int (*fp)(void *);              /* function we're going to call */
  131.         void *global_data;              /* global data reg (A4)         */
  132.         long return_code;               /* return code from process     */
  133.         struct FAKE_SegList *seg;       /* pointer to fake seglist so   */
  134.                                         /* can it can be free'd         */
  135.         void *UserData;                 /* User-supplied data pointer   */
  136.         };
  137.  
  138.  
  139. static void process_starter(void)
  140. {
  141.    struct Process *proc;
  142.    struct AmiProcMsg *mess;
  143.    __regargs int (*fp)(void *, void *, void *);
  144.          
  145.    proc = (struct Process *)FindTask((char *)NULL);
  146.  
  147.    /* get the startup message */
  148.    WaitPort(&proc->pr_MsgPort);
  149.    mess = (struct AmiProcMsg *)GetMsg(&proc->pr_MsgPort);
  150.  
  151.    /* gather necessary info from message */
  152.    fp = (__regargs int (*)(void *, void *, void *))mess->fp;
  153.  
  154.    /* replace this with the proper #asm for Aztec */
  155.    putreg(REG_A4, (long)mess->global_data);
  156.    
  157.    /* Allocate a new data section */
  158.    putreg(REG_A4, _CloneData());
  159.  
  160.    /* Run autoinitializers.  This has the effect of setting up    */
  161.    /* the standard C and C++ libraries (including stdio), running */
  162.    /* constructors for C++ externs and statics, and running user  */
  163.    /* autoinit functions.                                         */
  164.    __fpinit();
  165.  
  166.    /* Call the desired function */
  167.    /* We pass the UserData parameter three times in order to satisfy */
  168.    /* both PARM=REG and PARM=STACK function pointers.  Since we have */
  169.    /* declared the local 'fp' to be regargs, the three parms will go */
  170.    /* into A0, A1 and the stack.  If 'fp' points to a regargs func,  */
  171.    /* it will get its parm from A0 and all is well.  If 'fp' points  */
  172.    /* to a stdargs func, it will get its parameter from the stack and*/
  173.    /* all is still well.                                             */
  174.    mess->return_code = (*fp)(mess->UserData, mess->UserData, mess->UserData);
  175.  
  176.    /* Run autoterminators to clean up. */
  177.    __fpterm();
  178.    
  179.    /* Free the recently-allocated data section */
  180.    _FreeData();
  181.    
  182.    /* Forbid so the child can finish completely, before */
  183.    /* the parent cleans up.                             */
  184.    Forbid();
  185.  
  186.    /* Reply so process who spawned us knows we're done */   
  187.    ReplyMsg((struct Message *)mess);
  188.  
  189.    /* We finish without Permit()ing, but it's OK since our task    */
  190.    /* will end after the RTS, which will break the Forbid() anyway */
  191. }
  192.  
  193.  
  194. // AmiProc_Start - launch a new process with a specified function
  195. // pointer as the entry point.
  196. struct AmiProcMsg *AmiProc_Start(int (*fp)(void *), void *UserData)
  197. {
  198.    struct Process *process;
  199.    struct MsgPort *child_port;
  200.    struct AmiProcMsg *start_msg;
  201.    BPTR in, out;
  202. #ifndef NOOLDDOS
  203.    struct FAKE_SegList *seg_ptr;
  204. #endif
  205.    int stack = (__stack > 4000 ? __stack : 4000);
  206.    char *procname = (__procname ? __procname : "New Process");
  207.    
  208.    start_msg = (struct AmiProcMsg *)AllocMem(sizeof(struct AmiProcMsg), 
  209.                                           MEMF_PUBLIC|MEMF_CLEAR);
  210.    if (start_msg == NULL)
  211.       return NULL;
  212.  
  213. #ifndef NOOLDDOS
  214.    if(SysBase->LibNode.lib_Version > 36)
  215.    {
  216.       seg_ptr = NULL;  // We're not gonna use this, so null it
  217. #endif
  218.  
  219.       if(!(in  = Open("*", MODE_OLDFILE))) in  = Open("NIL:", MODE_NEWFILE);
  220.       if(!(out = Open("*", MODE_OLDFILE))) out = Open("NIL:", MODE_NEWFILE);
  221.  
  222.       /* Flush the data cache in case we're on a 68040 */
  223.       CacheClearU();
  224.  
  225.       process =   CreateNewProcTags(NP_Entry,     process_starter,
  226.                                     NP_StackSize,  stack,
  227.                                     NP_Name,       procname,
  228.                                     NP_Priority,   __priority,
  229.                                     NP_Input,      in,
  230.                                     NP_Output,     out,
  231.                                     TAG_END);
  232.       child_port = process ? &process->pr_MsgPort : NULL;
  233. #ifndef NOOLDDOS
  234.    }
  235.    else
  236.    {
  237.       /* We're running under AmigaDOS 1.3 or earlier. */
  238.       seg_ptr = (struct FAKE_SegList *)AllocMem(sizeof (*seg_ptr), MEMF_PUBLIC);
  239.       if (seg_ptr == NULL) 
  240.       {
  241.          FreeMem(start_msg, sizeof(*start_msg));
  242.          return NULL;
  243.       }
  244.  
  245.       /* Fill in Fake SegList */
  246.       seg_ptr->space = 0;
  247.       seg_ptr->length = (sizeof(*seg_ptr) + 3) & ~3;
  248.       seg_ptr->nextseg = NULL;
  249.    
  250.       /* Fill in JMP to function */
  251.       seg_ptr->jmp = 0x4EF9;  /* JMP instruction */
  252.       seg_ptr->func = process_starter;
  253.    
  254.       /* create the child process */   
  255.       child_port = CreateProc(procname,
  256.                               __priority,
  257.                               (BPTR)((long)&seg_ptr->nextseg>>2),
  258.                               stack);
  259.    }
  260. #endif
  261.  
  262.    if(child_port == NULL)
  263.    {
  264.       /* error, cleanup and abort */
  265. #ifndef NOOLDDOS
  266.       if(seg_ptr) FreeMem(seg_ptr, sizeof(*seg_ptr));
  267. #endif
  268.       FreeMem(start_msg, sizeof(*start_msg));
  269.       return NULL;
  270.    }
  271.    
  272.    /* Create the startup message */
  273.    start_msg->msg.mn_Length = sizeof(struct AmiProcMsg) - sizeof(struct Message);
  274.    start_msg->msg.mn_ReplyPort = CreatePort(0,0);
  275.    start_msg->msg.mn_Node.ln_Type = NT_MESSAGE;
  276.    
  277.    /* replace this with the proper #asm for Aztec */
  278.    start_msg->global_data = (void *)getreg(REG_A4);  /* save global data reg (A4) */
  279.  
  280. #ifndef NOOLDDOS
  281.    start_msg->seg = seg_ptr;
  282. #endif
  283.    start_msg->fp = fp;                               /* Fill in function pointer */
  284.    start_msg->UserData = UserData;   
  285.    
  286.    /* send startup message to child */
  287.    PutMsg(child_port, (struct Message *)start_msg);
  288.    
  289.    return start_msg;
  290. }
  291.  
  292. int AmiProc_Wait(struct AmiProcMsg *start_msg)
  293. {
  294.     struct AmiProcMsg *msg;
  295.     int ret;
  296.            
  297.     /* Wait for child to reply, signifying that it is finished */
  298.     while ((msg = (struct AmiProcMsg *)
  299.                    WaitPort(start_msg->msg.mn_ReplyPort)) != start_msg) 
  300.           ReplyMsg((struct Message *)msg);
  301.  
  302.     /* get return code */
  303.     ret = msg->return_code;
  304.  
  305.     /* Free up remaining resources */
  306.     DeletePort(start_msg->msg.mn_ReplyPort);
  307. #ifndef NOOLDDOS
  308.     if(start_msg->seg) 
  309.        FreeMem((void *)start_msg->seg, sizeof(struct FAKE_SegList));
  310. #endif
  311.     FreeMem((void *)start_msg, sizeof(*start_msg));
  312.  
  313.     return(ret);
  314. }
  315.  
  316.